package com.group.chat.web;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * author: lida
 * create time: 2020-10-16 22:39
 * description: websocket的具体实现类
 */
@Component
@ServerEndpoint(value = "/websocket/{nickname}")
public class MyWebSocket {

    //  日志
    private Logger logger = LoggerFactory.getLogger(MyWebSocket.class);

    //  用来存放每个客户端对应的 MyWebSocket 对象。
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<>();

    //  保存 频道号 和 session 的关系
    private static Map<String, Session> map = new ConcurrentHashMap<>();

    //  与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    //  用户昵称
    private String nickname;

    //  连接建立成功调用的方法
    @OnOpen
    public void onOpen(Session session, @PathParam("nickname") String nickname) {
        this.nickname = nickname;
        this.session = session;

        webSocketSet.add(this);

        map.put(session.getId(), session);

        logger.info("{}已连接 ,当前在线人数为： {} 人", nickname, webSocketSet.size());
        this.session.getAsyncRemote().sendText(nickname + "已连接上服务器，频道为" + session.getId() + "，当前在线人数为：" + webSocketSet.size());
    }

    //  连接关闭调用的方法
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        logger.info("{}已断开连接！当前在线人数为： {} 人", nickname, webSocketSet.size());
    }

    //  收到客户端消息后调用的方法
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("nickname") String nickname) {
        logger.info("来自 {} 的消息:  {}", nickname, message);

        //  从客户端传过来的数据是 json 数据，所以这里使用 jackson 进行转换为 SocketMsg 对象，
        //  然后通过 socketMsg 的 type 进行判断是 单聊 还是 群聊，进行相应的处理:
        ObjectMapper mapper = new ObjectMapper();
        SocketMsg socketMsg;

        try {
            socketMsg = mapper.readValue(message, SocketMsg.class);
            if (socketMsg.getType() == 1) {  //  单聊.需要找到 发送者 和 接受者
                socketMsg.setFromUser(session.getId()); //发送者.
                Session fromSession = map.get(socketMsg.getFromUser());
                Session toSession = map.get(socketMsg.getToUser());
                //  发送给接受者.
                if (toSession != null) {
                    fromSession.getAsyncRemote().sendText(nickname + "：" + socketMsg.getMsg());
                    toSession.getAsyncRemote().sendText(nickname + "：" + socketMsg.getMsg());
                } else {
                    //  发送给发送者
                    fromSession.getAsyncRemote().sendText("系统消息：对方不在线或者您输入的频道号不对");
                }
            } else { //  群发消息
                broadcast(nickname + ": " + socketMsg.getMsg());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

//        broadcast(nickname + ":" + message);
    }

    //  发生错误时调用
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("发生错误");
        error.printStackTrace();
    }

    //  群发自定义消息
    public void broadcast(String message) {
        for (MyWebSocket item : webSocketSet) {
            //同步异步说明参考：http://blog.csdn.net/who_is_xiaoming/article/details/53287691
            //this.session.getBasicRemote().sendText(message);
            item.session.getAsyncRemote().sendText(message);    //  异步发送消息.
        }
    }


}
